// HeavenII Library
// Copyright (C) HeavenII Team. All rights reserved.
// 
// This file is a part of the HeavenII Library.
// The use and distribution terms for this software are covered
// by the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
// which can be found in the file LICENSE at the root folder.

#pragma once

#include <functional>
#include <map>

//////////////////////////////////////////////////////////////////////////
// Classes in this file:
//
// HttpPacket
// HttpWebRequest
// ChromeHttpWebRequest
// HttpWebResponse
// HttpClient

// cHttpClient external API function linkage decorations.

#if defined(H2LIB_HTTPCLIENT_STATICLIB)
#   define H2LIB_HTTPCLIENT_EXTERN
#elif defined(WIN32) || defined(_WIN32)
#   if defined(BUILDING_H2LIB_HTTPCLIENT)
#       define H2LIB_HTTPCLIENT_EXTERN __declspec(dllexport)
#   else
#       define H2LIB_HTTPCLIENT_EXTERN __declspec(dllimport)
#   endif
#else
#       error "Only supported WIN32 platform"
#endif

namespace HeavenII
{
namespace net
{

//////////////////////////////////////////////////////////////////////////
// global function

/**
 * This function sets up the program environment that libcurl needs.
 **/
void H2LIB_HTTPCLIENT_EXTERN Initialize();

/**
 * This function releases resources
 **/
void H2LIB_HTTPCLIENT_EXTERN Cleanup();

typedef std::map<std::string, std::string> STRSTRMAP;


enum class RequestMethod
{
    Get,
    Post
};


enum class Encoding
{
    UNSET,
    ASCII,
    GBK,
    UTF8,
    UTF16
};


class H2LIB_HTTPCLIENT_EXTERN HttpPacket
{
public:
    HttpPacket();
    virtual ~HttpPacket();

    void AddHeader(const char *pszName, const char *pszValue);
    void AddHeader(const wchar_t *pszName, const wchar_t *pszValue);
    virtual void Clear();
    const char *GetEntity(size_t *pSizeInWords = nullptr) const;
    const char *GetHeader(const char *pszName) const;
    void SetEntity(const void *pEntity, size_t nSizeInWords = -1);
    void SetHeaders(STRSTRMAP *pHeaders);
    void EnumHeaders(std::function<void(const char *, const char *)> callback);
    //void EnumHeaders(std::function<void(const wchar_t *, const wchar_t *)> callback);

protected:
    std::string *m_pEntity;
    STRSTRMAP *m_pHeaders;

private:

};


class H2LIB_HTTPCLIENT_EXTERN HttpWebRequest : public HttpPacket
{
public:
    HttpWebRequest(const char *pszUrl, RequestMethod method = RequestMethod::Get);
    HttpWebRequest(const wchar_t *pszUrl, RequestMethod method = RequestMethod::Get);
    virtual ~HttpWebRequest();
    
    void EnableRedirect(bool bRedirect = true);
    RequestMethod GetMethod() const;
    const char *GetRequestUrl() const;
    bool IsAllowRedirect() const;
    void SetMethod(RequestMethod method);

protected:
    RequestMethod m_RequestMethod;
    bool m_bAllowRedirect;
    char m_szRequestUrl[1024];

};


class H2LIB_HTTPCLIENT_EXTERN ChromeHttpWebRequest : public HttpWebRequest
{
public:
    ChromeHttpWebRequest(const char *pszUrl, RequestMethod method = RequestMethod::Get);
    ChromeHttpWebRequest(const wchar_t *pszUrl, RequestMethod method = RequestMethod::Get);

};


class H2LIB_HTTPCLIENT_EXTERN HttpWebResponse : public HttpPacket
{
public:
    HttpWebResponse();
    HttpWebResponse(STRSTRMAP& headers, std::string& body, Encoding encoding);
    virtual ~HttpWebResponse();
    virtual void Clear();
    Encoding GetContentEncoding() const;
    const char *GetCookies() const;
    void SetContentEncoding(Encoding encoding);

protected:

private:
    Encoding m_Encoding;

};


class H2LIB_HTTPCLIENT_EXTERN HttpClient
{
public:
    HttpClient();
    ~HttpClient();

    /**
     * Get the timespan to wait before the request times out, in milliseconds.
     **/
    int GetTimeout() const;

    int GetResponse(HttpWebRequest& request, HttpWebResponse *pResonse = nullptr);

    /**
     * Set the timespan to wait before the request times out.
     * @param nMilliseconds The timespan to wait before the request times out, in milliseconds.
     **/
    void SetTimeout(int nMilliseconds);

protected:
    int m_nTimeout; //!< the timespan to wait before the request times out, in milliseconds.

private:
    static size_t OnWrite(char *pData, size_t size, size_t nmemb, std::string *pBufferStream);
    inline Encoding GetEncodingFromContentType(const char *pszContentType);
    inline void ParseHeaders(const char *lpszHeaders, STRSTRMAP& headers);
    inline void ParseCookies(const char *lpszHeaders, STRSTRMAP& cookies);

};

}
}
